/*******************************************************************************
 * Copyright (c) 2013, 2022 ACIN, Johannes Kepler University Linz
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 * Martin Melik Merkumians - initial API and implementation and/or initial documentation
 * Markus Meingast - refactoring and adaption to new Client class, enabling connection to multiple servers
 *******************************************************************************/

#pragma once

#include "MQTTHandler.h"
#include <string>
#include <vector>

#include "forte/arch/forte_sem.h"

extern "C" {
#include <MQTTAsync.h>
}

namespace forte::com_infra::mqtt_paho {

  class CMQTTClient {
    public:
      enum MQTTStates {
        NOT_CONNECTED,
        CONNECTION_ASKED,
        SUBSCRIBING,
        ALL_SUBSCRIBED,
      };

      CMQTTClient(const std::string &paAddress, const std::string &paClientId, MQTTHandler &paHandler);

      ~CMQTTClient();

      int initClient();

      bool runClient();

      void reconnect();

      const std::string &getClientId() const {
        return mClientId;
      }

      const std::string &getAddress() const {
        return mAddress;
      }

      MQTTStates getMQTTState() const {
        return mMQTT_STATE;
      }

      void addLayer(MQTTComLayer *paLayer);

      void removeLayer(MQTTComLayer *paLayer);

      int sendData(void *paData, unsigned int paSize, const std::string &paTopicName);

      int mqttSubscribe(const MQTTComLayer *paLayer);
      int mqttConnect();

      static void onMqttConnectionLost(void *paContext, char *paCause);
      static int onMqttMessageArrived(void *paContext, char *paTopicName, int paTopicLen, MQTTAsync_message *paMessage);

      static void onMqttConnectionSucceed(void *paContext, MQTTAsync_successData *paResponse);
      static void onMqttConnectionFailed(void *paContext, MQTTAsync_failureData *paResponse);

      static std::shared_ptr<CMQTTClient>
      getNewClient(const std::string &paAddress, const std::string &paClientId, MQTTHandler &paHandler);

    private:
      static void onSubscribeSucceed(void *paContext, MQTTAsync_successData *paResponse);
      static void onSubscribeFailed(void *paContext, MQTTAsync_failureData *paResponse);

      void removeLayerHelper(MQTTComLayer *paLayer, std::vector<MQTTComLayer *> &paLayerList);

      void removeToResubscribe(MQTTComLayer *paLayer);

      void clearToResubscribe() {
        mToResubscribe.clear();
      }

      static const int smKeepAliveInterval = 20;
      static const int smCleanSession = 1;

      static const int smTimeout = 10000;

      std::string mAddress;
      std::string mClientId;

      MQTTAsync mAsClient;
      MQTTAsync_connectOptions mClientConnectionOptions;
      MQTTStates mMQTT_STATE;
      forte::arch::CSyncObject mMQTTMutex;

      MQTTHandler &mHandler;
      std::vector<MQTTComLayer *> mLayers;
      std::vector<MQTTComLayer *> mToResubscribe;
  };

} // namespace forte::com_infra::mqtt_paho
